﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Workflow.Activities;
using System.Workflow.ComponentModel;
using Microsoft.Crm.Workflow;
using Microsoft.Crm.Sdk;
using System.Diagnostics;
using Microsoft.Crm.SdkTypeProxy;
using Microsoft.Crm.Sdk.Query;
using Microsoft.Crm.Sdk.Metadata;
using Microsoft.Crm.SdkTypeProxy.Metadata;

namespace Infusion.CRM.WF.CRM40DistributeWF
{

    /// <summary>
    /// Share a record with specified users/teams.
    /// </summary>
    [CrmWorkflowActivity("Distribute Workflow (N:N)", "Utilities")]
    public class DistributeWFActivityManytoMany : SequenceActivityBase
    {

        #region Workflow Parameters

        /// <summary>
        /// Input parameter: Workflow to be executed.
        /// </summary> 
        public static DependencyProperty WorkflowProperty = DependencyProperty.Register("Workflow", typeof(Lookup), typeof(DistributeWFActivityManytoMany));
        [CrmInput("Distributed Workflow")]
        [CrmReferenceTarget("workflow")]
        public Lookup Workflow
        {
            get
            {
                return (Lookup)base.GetValue(WorkflowProperty);
            }
            set
            {
                base.SetValue(WorkflowProperty, value);
            }
        }

        /// <summary>
        /// Name of the relationship as specified in the customization corresponding field.
        /// </summary>
        public static DependencyProperty RelationshipNameProperty = DependencyProperty.Register("RelationshipName", typeof(string), typeof(DistributeWFActivityManytoMany));
        [CrmInput("Relationship Name")]
        public string RelationshipName
        {
            get
            {
                return (string)base.GetValue(RelationshipNameProperty);
            }
            set
            {
                base.SetValue(RelationshipNameProperty, value);
            }
        }

        /// <summary>
        /// Output Parameter: True if operation was successful. If False, Error Message will be populated.
        /// </summary>
        public static DependencyProperty SuccessProperty = DependencyProperty.Register("Success", typeof(CrmBoolean), typeof(DistributeWFActivityManytoMany));
        [CrmOutput("Success")]
        public override CrmBoolean Success
        {
            get
            {
                return (CrmBoolean)base.GetValue(SuccessProperty);
            }
            set
            {
                base.SetValue(SuccessProperty, value);
            }
        }

        /// <summary>
        /// Output Parameter: Error Message is only populated when Success == false.
        /// </summary>
        public static DependencyProperty ErrorMsgProperty = DependencyProperty.Register("ErrorMsg", typeof(string), typeof(DistributeWFActivityManytoMany));
        [CrmOutput("Error Message")]
        public override string ErrorMsg
        {
            get
            {
                return (string)base.GetValue(ErrorMsgProperty);
            }
            set
            {
                base.SetValue(ErrorMsgProperty, value);
            }
        }

        #endregion

        #region SequenceActivity

        protected override CrmBoolean ExecuteBody(Context context, out string errorMsg)
        {
            errorMsg = null;

            if (this.Workflow == null)
            {
                throw new ArgumentNullException("Workflow");
            }
            this.Distribute(context, this.GatherKeys(context), this.Workflow.Value);

            return new CrmBoolean(true);
        }

        #endregion

        #region Local Methods

        private ManyToManyMetadata GetRelationship(Context context)
        {
            RetrieveRelationshipRequest relationshipRequest = new RetrieveRelationshipRequest();
            relationshipRequest.Name = this.RelationshipName;
            relationshipRequest.RetrieveAsIfPublished = false;

            RetrieveRelationshipResponse relationshipResponse = (RetrieveRelationshipResponse)context.SvcMetadataHandle.Execute(relationshipRequest);
            if (relationshipResponse.RelationshipMetadata.RelationshipType != EntityRelationshipType.ManyToMany)
            {
                throw new ArgumentException("Relationship is not Many to Many", "Relationship Name");
            }

            return (ManyToManyMetadata)relationshipResponse.RelationshipMetadata;
        }

        private List<KeyProperty> GatherKeys(Context context)
        {
            ManyToManyMetadata relationship = this.GetRelationship(context);

            string primaryAttribute;
            string secondaryEntity;
            string secondaryAttribute;
            string intersection = relationship.IntersectEntityName;
            if (relationship.Entity1LogicalName == context.CurrentEntityName)
            {
                // entity1 is primary
                primaryAttribute = relationship.Entity1IntersectAttribute;
                secondaryEntity = relationship.Entity2LogicalName;
                secondaryAttribute = relationship.Entity2IntersectAttribute;
            }
            else
            {
                // entity2 is primary
                primaryAttribute = relationship.Entity2IntersectAttribute;
                secondaryEntity = relationship.Entity1LogicalName;
                secondaryAttribute = relationship.Entity1IntersectAttribute;
            }

            QueryExpression query = new QueryExpression();
            LinkEntity secondaryToIntersection = new LinkEntity();
            LinkEntity intersectionToPrimary = new LinkEntity();
            ConditionExpression primaryCondition = new ConditionExpression();

            // Chain all links
            query.EntityName = secondaryEntity;
            query.LinkEntities.Add(secondaryToIntersection);
            secondaryToIntersection.LinkEntities.Add(intersectionToPrimary);
            intersectionToPrimary.LinkCriteria.Conditions.Add(primaryCondition);

            // First link
            secondaryToIntersection.LinkToEntityName = intersection;
            secondaryToIntersection.LinkFromAttributeName =
            secondaryToIntersection.LinkToAttributeName = secondaryAttribute;

            // Second link
            intersectionToPrimary.LinkToEntityName = context.CurrentEntityName;
            intersectionToPrimary.LinkFromAttributeName =
            intersectionToPrimary.LinkToAttributeName = primaryAttribute;

            // Condition
            primaryCondition.AttributeName = primaryAttribute;
            primaryCondition.Operator = ConditionOperator.Equal;
            primaryCondition.Values = new object[] { context.CurrentRecordId.ToString() };

            RetrieveMultipleRequest retrieveRequest = new RetrieveMultipleRequest();
            retrieveRequest.Query = query;
            retrieveRequest.ReturnDynamicEntities = true;

            List<KeyProperty> list = new List<KeyProperty>();
            RetrieveMultipleResponse retrieveResponse = (RetrieveMultipleResponse)context.SvcHandle.Execute(retrieveRequest);
            foreach (BusinessEntity be in retrieveResponse.BusinessEntityCollection.BusinessEntities)
            {
                DynamicEntity de = (DynamicEntity)be;
                foreach (Property prop in de.Properties)
                {
                    KeyProperty key = (KeyProperty)prop;
                    list.Add(key);
                    break;
                }
            }

            return list;
        }

        private void Distribute(Context context, List<KeyProperty> keyList, Guid workflowId)
        {
            foreach (KeyProperty key in keyList)
            {
                ExecuteWorkflowRequest workflowRequest = new ExecuteWorkflowRequest();
                workflowRequest.EntityId = key.Value.Value;
                workflowRequest.WorkflowId = workflowId;
                context.SvcHandle.Execute(workflowRequest);
            }
        }

        #endregion

    }
}
